import socket
import select
import queue

from core.server import EOM

class Server():
    def __init__(self):
        self.s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
        self.s.setblocking(0)
        self.s.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR, 1)#可以复用地址
        self.sockets: dict(int, socket.socket)= {} # key:file desc value:socket
        self.messageQ: dict(int, queue.Queue) = {} # key:file desc value:Queue
        self.e = select.epoll()
        self.e.register(self.s.fileno(), select.EPOLLIN)

    def start(self):
        self.s.bind(("0.0.0.0",8888))
        self.s.listen(20)
        while True:
            result_list = self.e.poll(10)
            for fd, event in result_list: # 这里循环Epoll响应的内容
                print("%d %d \n"%(fd, event))
                if select.EPOLLHUP & event: # epoll响应的结束事件
                    #这里是结束事件
                    self.end(fd, event)
                elif select.EPOLLIN & event: # epoll响应的 读取事件
                    if fd == self.s.fileno():
                    #读取 server 内容
                        #server socket need to create connection.
                        clientS,addr = self.s.accept() # 接受到client sockeet
                        clientS.setblocking(0)
                        self.sockets[clientS.fileno()] = clientS
                        self.messageQ[clientS.fileno()] = queue.Queue()
                        self.e.register(clientS.fileno(), select.EPOLLIN | select.EPOLLHUP) # 把client socket的epool in的方法注册到epoll
                    else:
                        #client
                        content = ""
                        while True:
                            try:
                                content += self.sockets[fd].recv(1024).decode("utf-8")
                            except Exception as e:
                                print(e)
                                break
                            if content == "" or content.endswith(EOM):
                                break
                        if content == "":
                            self.end(fd, self.sockets[fd])
                            continue
                        #TODO anything 这里可以对结果操作
                        print(content)
                        for connfd, conn in self.sockets.items():
                            self.messageQ[connfd].put(content) #把结果内容放到我们的messageQ，方便下面的elif event & select.EPOLLOUT
                            self.e.modify(connfd, select.EPOLLIN | select.EPOLLOUT) #修改事件为读取和写入
                elif select.EPOLLOUT & event:
                    #写事件
                    try:
                        content = self.messageQ[fd].get_nowait()
                    except queue.Empty:
                        print("error")
                        self.e.modify(fd,select.EPOLLHUP)
                        continue
                    else:
                        self.sockets[fd].send(content.encode("utf-8")) # 发送信息给client socket
                        self.e.modify(fd, select.EPOLLIN) # 修改fd的事件为 读取

    def end(self,fd: int, s :socket.socket):
        print("client close!")
        del self.sockets[fd]
        del self.messageQ[fd]
        self.e.unregister(fd)
        s.close()


if __name__ == "__main__":
    Server().start()